import {
  CENTEREVENTS,
  CSPoint,
  DocumentEvents,
  DualController,
  EventsRes,
  IAssemblyDocTreeNode,
  Icon,
  IdObj,
  MouseInfo,
  PickCurve,
  PickedFace,
  pickedFaceData,
  pickedLineData,
  pickedModelData,
  PickedObject,
  pickedVertexData,
  PickLine,
  PickType,
  POINT_TYPE,
  ReferenceCsys,
  ReferenceLine,
  ReferencePlane,
  STATE,
  TrackBallControlState,
  Types,
  useCommandManager,
} from '@amcax/base';
import { applyMatrix4ArrayToObject } from '@amcax/renderer';
import { ArrowsAltOutlined } from '@ant-design/icons';
import { ToolbarMouseControlManager } from '@app-base/common/commands/cmd-toolbar-mouse-control';
import { get1stLevelPartFromId } from '@app-base/environments/product/utils';
import {
  CAD_EVENT,
  PRODUCT_EVENT,
  SHAPE_CHANGE_PENDING_EVENT_HANDLED,
} from '@app-cad/common/Constants';
import { getPickOptions } from '@app-cad/common/components/pick-input/3d.utils';
import { MouseActionInput } from '@app-cad/common/components/pick-input/mouse-action-input';
import { csEvent } from '@app-cad/common/events/EventCenter';
import { useTools } from '@app-cad/common/hooks/useTools';
import { DocumentContext } from '@app-cad/common/providers/documentProvider';
import {
  getDocumentObjectRenderType,
  getIdOfPickedObj,
  getNameOfPickedObj,
} from '@app-cad/common/utils/render';
import { cmdMgr } from '@app-cad/manager/CmdManager';
import {
  ctx2,
  dataManager,
  docMgr,
  referenceManager,
  userEvents,
} from '@app-cad/module';
import {
  allParts,
  getPartRefKey,
  getProductDocumentObject,
} from '@app-cad/pages/product-support/part-funcs';
import { useMemoizedFn, usePrevious } from 'ahooks';
import { isEqual } from 'lodash';
import { useContext, useEffect, useRef, useState } from 'react';
import { TreeNodeDocumentObject } from '../tree-context/types';
import { generateProductPickObj, PickedProduct } from './pick-product';
import './style.scss';
import {
  BasePickInputProps,
  MultiPickResult,
  pickedLawCurveData,
  pickedProductData,
  PickInputEvents,
  PickInputValueType,
} from './types';

export * from './types';

export const getMultiPickResultUUID = (pickResult: MultiPickResult) =>
  (pickResult as pickedLineData | pickedVertexData | pickedModelData).assisObj
    ?.uuid ??
  (pickResult as pickedFaceData).assisObj?.mesh.uuid ??
  pickResult.idInfo?.o;

const isIdInfoFrame = (idInfo: IdObj) => {
  const documentObject = docMgr.getDocumentObjectById(idInfo?.o);

  return documentObject?.type === Types.FRAME && idInfo?.i == null;
};

// 若需要临时禁用pickInput的拾取，在此处修改
export const pickInput3dLock = {
  isLocked: false,
  lock() {
    this.isLocked = true;
  },
  unlock() {
    this.isLocked = false;
  },
};

type ProductRelationNode = {
  id: string;
  parent: ProductRelationNode;
  parents: string[];
  children: ProductRelationNode[];
};

const clonePreviewObject = (v: MultiPickResult | null, isPart: boolean) => {
  const isFrame = isPart && isIdInfoFrame(v?.idInfo);

  // LineSegmentsGeometry2对clone的支持不好，重新创建
  if (isFrame) {
    const referenceTarget = referenceManager.getObjectById(v.idInfo.o);

    if (referenceTarget instanceof ReferenceCsys) {
      const result = referenceTarget.generatePickedObj();
      result.selected();
      return result;
    }
  }

  const result = v?.assisObj?.cloneSelf();

  // apply matrix4 array for product part
  if (!isPart && !(result instanceof PickedProduct)) {
    applyMatrix4ArrayToObject(
      result,
      allParts.get(v?.idInfo?.o?.split?.(':')?.[0])?.matrix4Array,
    );
  }

  return result;
};

export function MultiPickInput<T extends MultiPickResult = MultiPickResult>(
  props: BasePickInputProps<T[]>,
) {
  const {
    onPick,
    nameRenderer = getNameOfPickedObj,
    onCurvedLawPick,
    value,
    controlled = true,
    allowPickType = [
      PickInputValueType.Surface,
      PickInputValueType.Plane,
      PickInputValueType.Line,
      PickInputValueType.Curve,
      PickInputValueType.Vertex,
      PickInputValueType.Model,
      PickInputValueType.ALL,
      PickInputValueType.Frame,
      PickInputValueType.Sketch,
      PickInputValueType.LawCurve,
      PickInputValueType.PartArray,
      PickInputValueType.TempFace,
    ],
    disallowedPickType = [],
    isMulti = false,
    disabled = false,
    showArrow = false,
    curveReversable = false,
    active,
    setActive,
    allowInfiniteLine,
    allowInfinitePlane,
    ignoreGlobalPickFilter = false,
    onFocus,
    onBlur,
    pickFilter,
    allowMarqueeSelect = true,
    alwaysPreserverSuffix = false,
    highlightSelection = true,
    allowPickInvisibleAssembleFromLeftPanel = false,
    noDuplicateProduct = false,
    ...rest
  } = props;

  const onPickWithoutDuplicateProdcuct = (
    picked?: T[],
    ev?: EventsRes<PointerEvent>,
    needFocusNext?: boolean,
  ) => {
    if (onPick == null) {
      return;
    }
    if ((allowProduct || allowFullProduct) && noDuplicateProduct) {
      const _picked = picked.filter((p) => {
        const result = productTreeRelationData.current[p.idInfo.o];
        return picked.every((_p) => result && !result.includes(_p.idInfo.o));
      });
      onPick(_picked, ev, needFocusNext);

      return;
    }
    onPick(picked, ev, needFocusNext);
  };

  const { docTree } = useContext(DocumentContext);
  const { currentCmdProps } = useCommandManager();

  const productTreeRelationData = useRef<Record<string, string[]>>({});

  const updatePartTreeRelationData = () => {
    const walk = (node: IAssemblyDocTreeNode, parent?: ProductRelationNode) => {
      const relationNode = {
        id: node.key,
        parent,
        parents: parent == null ? [] : [...parent.parents, parent.id],
        children: [],
      };

      relationNode.children = (node.children ?? [])
        .filter(
          (node) =>
            node.type === 'part' ||
            node.type === 'product' ||
            node.type === 'array',
        )
        .map((node) => walk(node, relationNode));

      productTreeRelationData.current[relationNode.id] = relationNode.parents;

      return relationNode;
    };

    walk(docTree);
  };

  useEffect(() => {
    if (!isPart) {
      updatePartTreeRelationData();
    }
  }, [docTree]);

  const [innerIsActive, setInnerIsActive] = useState(false);

  const isActive = active ?? innerIsActive;

  const setIsActive = (val: boolean) => {
    (setActive ?? setInnerIsActive)(val);
  };

  // state允许作为非受控组件使用
  const [pickedObjects, setPickedObjects] = useState<T[]>([]);
  const [pickedLawCurve, setPickedLawCurve] = useState<pickedLawCurveData>({
    // temporarily set to the first object in the value array
    // TODO: support multiple law curves if isMulti is true
    title: (value?.[0] as any)?.objInfo?.label ?? '',
    dxid: (value?.[0] as any)?.objInfo?.dxid ?? '',
  });

  const pickedData = controlled ? (value ?? []) : (value ?? pickedObjects);

  // 用于记录和销毁选中高亮
  const previewRef =
    useRef<
      (
        | CSPoint
        | PickLine
        | PickCurve
        | PickedFace
        | PickedObject
        | PickedProduct
      )[]
    >();

  const { picker: picker3d, pickerAssembly, renderer } = useTools();

  const { documentInfo } = useContext(DocumentContext);
  const isPart = documentInfo.type === 'part';

  const picker = isPart ? picker3d : pickerAssembly;
  const getDocumentObjectById = (id: string) =>
    isPart
      ? docMgr.getDocumentObjectById(id?.split?.(':')?.[0])
      : getProductDocumentObject(id);
  const {
    allowVertex,
    allowPlane,
    allowSurface,
    allowLine,
    allowCurve,
    allowModel,
    allowALL,
    allowSketch,
    allowFrame,
    allowProduct,
    allowFullProduct,
    allowLawCurve,
    allowPartArray,
  } = getPickOptions(allowPickType, disallowedPickType);

  const allowMesh = allowPlane || allowSurface;
  const allowEdge = allowLine || allowCurve;

  const showSketchPickFace = () => {
    if (isPart && isActive && allowSketch && allowMesh) {
      renderer.showSketchPickFaces();
    }
  };

  useEffect(() => {
    showSketchPickFace();
    userEvents.on(SHAPE_CHANGE_PENDING_EVENT_HANDLED, showSketchPickFace);

    return () => {
      userEvents.off(SHAPE_CHANGE_PENDING_EVENT_HANDLED, showSketchPickFace);
      if (isPart) {
        renderer.hideSketchPickFaces();
      }
    };
  }, [isActive]);

  // 默认情况下，仅允许平面的选择器允许选择无限平面，仅允许选择直线的选择器允许选择无限直线
  const planeOnly = allowPlane && !allowSurface;
  const straitLineOnly = allowLine && !allowCurve;

  const innerAllowInfiniteLine = allowInfiniteLine ?? straitLineOnly;
  const innerAllowInfinitePlane = allowInfinitePlane ?? planeOnly;

  const hoverFrame = useRef<PickedObject>();
  const hoverProduct = useRef<PickedProduct>();

  const pickByEvent = (e: EventsRes<PointerEvent>, isSelect = false) => {
    if (pickInput3dLock.isLocked) {
      return;
    }

    const currentEditDxid =
      currentCmdProps?.shape || (currentCmdProps as any)?.dxid;

    const targetDocumentObject = getDocumentObjectById(currentEditDxid);

    const pickableParams = ignoreGlobalPickFilter
      ? null
      : currentCmdProps && {
          pickable: false,
          dxids: [
            targetDocumentObject?.dxid,
            ...Array.from(
              targetDocumentObject?.recursiveOutlinks ?? new Set<string>(),
            ),
          ].filter((v) => v != null),
        };

    if (allowFrame && allowPickType.length === 1) {
      const res: MultiPickResult = picker.pick(
        e,
        {
          line: true,
          face: true,
          planeOnly: true,
          straitLineOnly: true,
        },
        {
          allowInfiniteLine: true,
          allowInfinitePlane: true,
          noHighlight: true,
          pickable: pickableParams,
        },
      );

      const dxid = res?.idInfo?.o;
      const target = referenceManager.getObjectById(dxid);

      const documentObject = getDocumentObjectById(dxid);

      // 拾取坐标系：暂时只支持allowPickType为单类型坐标系的场景
      if (
        documentObject?.type === Types.FRAME &&
        target instanceof ReferenceCsys
      ) {
        const result = picker.generatePickObjByPickID(dxid) as pickedModelData;
        if (isSelect) {
          return result;
        }
        if (hoverFrame.current !== result?.assisObj) {
          ctx2.cadScene.assisGroup.remove(hoverFrame.current);
          hoverFrame.current = result?.assisObj;
          hoverFrame.current?.selected();
          hoverFrame.current?.addTo(ctx2.cadScene.assisGroup);
        }
        return result;
      } else {
        ctx2.cadScene.assisGroup.remove(hoverFrame.current);
        hoverFrame.current?.destroy();
        return null;
      }
      // 拾取装配/零件：暂时只支持allowPickType为装配/零件+总装配的场景
    } else if (
      allowProduct &&
      allowPickType.every((t) =>
        [PickInputValueType.FullProduct, PickInputValueType.Product].includes(
          t,
        ),
      )
    ) {
      const res: MultiPickResult = picker.pick(
        e,
        {
          model: true,
        },
        {
          noHighlight: true,
          pickable: pickableParams,
        },
      );

      const dxid = res?.idInfo?.o;

      const target = get1stLevelPartFromId(dxid);

      const result = generateProductPickObj(
        target,
        res?.mouse,
      ) as pickedProductData;

      if (isSelect) {
        ctx2.cadScene.assisGroup.remove(hoverProduct.current);
        hoverProduct.current?.destroy();
        hoverProduct.current = null;
        res?.assisObj?.destroy();
        return result;
      }

      if (result != null) {
        if (hoverProduct.current?.assembly?.key !== target?.key) {
          ctx2.cadScene.assisGroup.remove(hoverProduct.current);
          hoverProduct.current = result.assisObj;
        }

        hoverProduct.current?.addTo(ctx2.cadScene.assisGroup);

        return result;
      }

      ctx2.cadScene.assisGroup.remove(hoverProduct.current);
      hoverProduct.current?.destroy();
      hoverProduct.current = null;
      return null;
    } else {
      const result = picker.pick(
        e,
        {
          point: allowVertex,
          line: allowEdge,
          face: allowMesh,
          model: !allowEdge && !allowMesh && allowModel,
          all: allowALL,
          planeOnly,
          straitLineOnly,
        },
        {
          showArrow,
          allowInfiniteLine: innerAllowInfiniteLine,
          allowInfinitePlane: innerAllowInfinitePlane,
          noHighlight: isSelect,
          pickable: pickableParams,
        },
      );
      return result;
    }
  };

  const marqueeFlagRef = useRef(false);
  const pointDownPositionRef = useRef<MouseInfo>();
  const marqueePreviewsRef = useRef<T[]>([]);

  const onMouseMove = useMemoizedFn((e: EventsRes<PointerEvent>) => {
    if (e.button?.onLeft) {
      if (
        isMulti &&
        allowMarqueeSelect &&
        ToolbarMouseControlManager.state === TrackBallControlState.NONE
      ) {
        const res = picker.marqueePick(e, {
          point: allowVertex || allowALL,
          line: allowEdge || allowALL,
          face: allowMesh || allowALL,
          model: allowModel,
        });

        marqueePreviewsRef.current.forEach((v) => {
          if (res == null || !res.includes(getIdOfPickedObj(v))) {
            v.assisObj.destroy();
          }
        });

        if (res != null) {
          const newRes = res
            .filter(
              (v) =>
                !marqueePreviewsRef.current.some(
                  (p) => getIdOfPickedObj(p) === v,
                ),
            )
            .map((v) => picker.generatePickObjByPickID(v)) as T[];

          marqueePreviewsRef.current = [
            ...marqueePreviewsRef.current.filter((v) => {
              return res.includes(getIdOfPickedObj(v));
            }),
            ...newRes,
          ];

          newRes.forEach((v) => {
            if (!isPart) {
              applyMatrix4ArrayToObject(
                v.assisObj,
                allParts.get(v.idInfo.o.split(':')[0])?.matrix4Array,
              );
            }

            v.assisObj.addTo(ctx2.cadScene.assisGroup);
          });

          ctx2.viewer.requestRender();
        }

        marqueeFlagRef.current = true;
      }
    } else {
      marqueeFlagRef.current = false;
      pickByEvent(e);
    }
  });

  const clearPreview = () => {
    if (previewRef.current != null) {
      previewRef.current.forEach((o) => {
        o.destroy();
      });
      ctx2.viewer.requestRender();
    }
  };

  useEffect(() => clearPreview, []);

  const preview = () => {
    clearPreview();

    if (!highlightSelection) {
      return;
    }

    previewRef.current = pickedData
      .map((o) => clonePreviewObject(o, isPart))
      .filter((v) => v != null);

    previewRef.current.forEach((o) => {
      if (o.name === 'cspoint') {
        (o as CSPoint).setType(POINT_TYPE.SELECT_POINT);
      }
      if (o instanceof PickLine || o instanceof PickCurve) {
        o.update({ showArrow, lineId: o.lineId });
      }
      o.addTo(ctx2.cadScene.assisGroup);
    });

    ctx2.viewer.requestRender();
  };

  const onMouseUp = useMemoizedFn((e: EventsRes<PointerEvent>) => {
    if (e.button.onLeft) {
      const threshold = 20;

      picker.marqueePointerUp();
      marqueePreviewsRef.current.forEach((v) => {
        v.assisObj.destroy();
      });

      const { x, y } = pointDownPositionRef.current ?? {};
      const distance = Math.sqrt((e.mouse?.x - x) ** 2 + (e.mouse?.y - y) ** 2);

      if (
        marqueeFlagRef.current &&
        pointDownPositionRef.current &&
        distance > threshold
      ) {
        marqueeFlagRef.current = false;

        onPickNew(marqueePreviewsRef.current);
        marqueePreviewsRef.current = [];
      } else {
        const pickRes: T | undefined = pickByEvent(e, true);

        if (pickRes?.idInfo == null) {
          return;
        }

        // 不是pickAll的情况
        if (!allowALL && !allowModel) {
          if (
            (pickRes.idInfo.t === PickType.CURVE && !allowCurve) ||
            (pickRes.idInfo.t === PickType.SURFACE && !allowSurface)
          ) {
            return;
          }
        }

        // picker对pick结果
        // pick得到的结果对象和内部的assisObj可能被原地修改，此处需要做clone
        const clonedPickRes = {
          ...pickRes,
          assisObj: clonePreviewObject(pickRes, isPart),
        } as T;

        onPickNew([clonedPickRes], e);
      }
    }
  });

  const onMouseDown = useMemoizedFn((e: EventsRes<PointerEvent>) => {
    if (e.button.onLeft) {
      pointDownPositionRef.current = e.mouse;
    }
  });

  const onPickNew = (_targets: T[], ev?: EventsRes<PointerEvent>) => {
    const targets = _targets.filter((t) => pickFilter == null || pickFilter(t));

    targets.forEach((target) => {
      // 如果是只有一个点/线/面的拾取结果，就去掉最后的:(f|e|v)0
      // 草图为例外，草图即使只有一个线/面，也可能有独立的点/线
      let pickID = getIdOfPickedObj(target);
      const documentObject = getDocumentObjectById(pickID);
      if (
        /:(e|f|v)0$/.test(pickID) &&
        documentObject?.type !== Types.Sketcher &&
        !alwaysPreserverSuffix
      ) {
        const dxid = target.idInfo.o;
        const docObj = getDocumentObjectById(dxid);
        const pickType = getDocumentObjectRenderType(
          docObj,
          (target as any)?.objInfo?.partKey,
        );

        if (pickType === PickType.VERTEX && pickID.endsWith(':v0')) {
          const vertexTarget = target as pickedVertexData;
          vertexTarget.assisObj.modelDxid =
            vertexTarget.assisObj.modelDxid.replace(':v0', '');
          vertexTarget.vertexId = vertexTarget.vertexId.replace(':v0', '');
        }

        if (
          [PickType.CURVE, PickType.LINE].includes(pickType) &&
          pickID.endsWith(':e0')
        ) {
          const lineTarget = target as pickedLineData;
          lineTarget.edgeId = lineTarget.edgeId.replace(':e0', '');
        }

        if (
          [PickType.SURFACE, PickType.PLANE].includes(pickType) &&
          pickID.endsWith(':f0')
        ) {
          const faceTarget = target as pickedFaceData;
          faceTarget.assisObj.modelDxid = faceTarget.assisObj.modelDxid.replace(
            ':f0',
            '',
          );
          faceTarget.meshId = faceTarget.meshId.replace(':f0', '');
        }
      }

      pickID = getIdOfPickedObj(target);
      userEvents.emit(PickInputEvents.TargetPicked, pickID);
    });

    let newResult: T[] = [];

    if (!isMulti) {
      newResult = targets;
    } else {
      const pickedIds = new Set(pickedData.map((v) => getIdOfPickedObj(v)));

      newResult = [
        ...pickedData,
        ...targets.filter((t) => !pickedIds.has(getIdOfPickedObj(t))),
      ];
    }

    newResult = newResult.filter((v) => v != null);

    onPickWithoutDuplicateProdcuct?.(newResult, ev, true);
    setPickedObjects(newResult);
  };

  useEffect(() => {
    userEvents.emit(PickInputEvents.TargetPicked); // send undefined to indicate that no target picked
    clearPreview();
  }, []);

  useEffect(() => {
    if (disabled) {
      unRegister();
    }
  }, [disabled]);

  const previousValue = usePrevious(value);

  useEffect(() => {
    if (!controlled && previousValue != null && value == null) {
      clearPreview();
    } else {
      preview();
    }
  }, [previousValue, pickedData, value, controlled, highlightSelection]);

  // todo 暂时写一个对于法则曲线支持的函数 需要将来放在 onTreeViewSelected 中

  const getPickedObjectFromTreeNode = useMemoizedFn(
    async (target: TreeNodeDocumentObject) => {
      // 获取对象类型
      const type = target?.type as Types;
      // 如果目标为空或类型为GeomBox，直接返回null
      if (target == null || type === Types.GeomBox) {
        return null;
      }

      // 如果对象不可见，先拉取依赖的形状信息
      if (target.isRecursiveVisible === false) {
        await cmdMgr.fetchDependentShapeInfo([target.shape]);
      }

      // 法则曲线类型且允许拾取法则曲线时，特殊处理
      if (type === Types.LAW_CURVE && allowLawCurve) {
        const newResult = {
          dxid: target?.dxid,
          title: target?.label,
        };
        onCurvedLawPick?.(newResult, null, true);
        setPickedLawCurve(newResult);
        return null;
      }

      // 坐标系类型且允许拾取坐标系时，生成拾取对象
      // TODO: sdk内增加FRAME的assisObj
      if (type === Types.FRAME && allowFrame) {
        return picker.generatePickObjByPickID((target as any).dxid) as T;
      }

      // 草图类型且允许拾取草图时，生成拾取对象
      if (type === Types.Sketcher && allowSketch) {
        return picker.generatePickObjByPickID(target.dxid) as T;
      }

      // 获取渲染类型，not used in assembly environment, no partKey needed
      const pickType = getDocumentObjectRenderType(target);

      // 顶点类型且允许拾取顶点或全部类型时，生成拾取对象
      if (pickType === PickType.VERTEX && (allowVertex || allowALL)) {
        return picker.generatePickObjByPickID(target.shape || target.dxid) as T;
      }

      // 线/曲线类型且允许拾取边或全部类型时，生成拾取对象
      if (
        [PickType.CURVE, PickType.LINE].includes(pickType) &&
        (allowEdge || allowALL)
      ) {
        const isCurve = pickType === PickType.CURVE;

        // 如果是曲线但不允许拾取曲线，则返回null
        if (isCurve && !(allowCurve || allowALL)) {
          return null;
        }

        // 不允许无限直线时，过滤掉参考线和FRAME类型
        if (
          !innerAllowInfiniteLine &&
          (referenceManager.getObjectById(
            target.shape || target.dxid,
          ) instanceof ReferenceLine ||
            (isPart &&
              docMgr.getDocumentObjectById(
                (target.shape || target.dxid).replace(/:.*$/, ''),
              )?.type === Types.FRAME))
        ) {
          return null;
        }

        // 生成拾取对象，带showArrow参数
        return picker.generatePickObjByPickID(target.shape || target.dxid, {
          showArrow,
        }) as T;
      }

      // 面/平面类型且允许拾取网格或全部类型时，生成拾取对象
      if (
        [PickType.SURFACE, PickType.PLANE].includes(pickType) &&
        (allowMesh || allowALL)
      ) {
        // 是否是曲面（非平面）
        const isSurface = pickType === PickType.SURFACE;

        // 如果是曲面但不允许拾取曲面，则返回null
        if (isSurface && !(allowSurface || allowALL)) {
          return null;
        }

        // 不允许无限平面时，过滤掉参考平面和FRAME类型
        if (
          !innerAllowInfinitePlane &&
          (referenceManager.getObjectById(
            target.shape || target.dxid,
          ) instanceof ReferencePlane ||
            (isPart &&
              docMgr.getDocumentObjectById(
                (target.shape || target.dxid).replace(/:.*$/, ''),
              )?.type === Types.FRAME))
        ) {
          return null;
        }

        // 生成拾取对象
        return picker.generatePickObjByPickID(target.shape || target.dxid) as T;
      }

      // 获取形状数据
      const shape = dataManager.getShapeData(
        target.shape,
        getPartRefKey(target.assembly),
      );

      // 允许模型或全部类型且有形状数据，或允许拾取零件内的阵列时，生成拾取对象
      if (
        ((allowModel || allowALL) &&
          (shape != null || dataManager.isReferenceObject(target))) ||
        (allowPartArray && target.type === Types.PART_ARRAY)
      ) {
        return picker.generatePickObjByPickID(target.shape || target.dxid) as T;
      }
    },
  );

  const onTreeViewSelected = useMemoizedFn(
    async (target: TreeNodeDocumentObject) => {
      const pickedObject = await getPickedObjectFromTreeNode(target);
      if (pickedObject) {
        onPickNew([pickedObject]);
      }
    },
  );

  const onTreeViewMultiSelected = useMemoizedFn(
    async (targets: TreeNodeDocumentObject[]) => {
      const pickPromises = targets.map((target) =>
        getPickedObjectFromTreeNode(target),
      );
      const pickedObjects = (await Promise.all(pickPromises)).filter(Boolean);
      if ((pickedObjects.length > 1 && isMulti) || pickedObjects.length === 1) {
        onPickNew(pickedObjects);
      }
    },
  );

  const onAssemblyTreeViewSelected = useMemoizedFn(
    async (target: IAssemblyDocTreeNode) => {
      onPickNew([]);

      if (['YOZ', 'XOZ', 'XOY'].includes(target.key) && allowPlane) {
        const pickObj = picker.generatePickObjByPickID(target.key) as T;
        if (pickObj) {
          onPickNew([pickObj]);
        }
        return;
      }

      if (
        (target.key === docTree.key && !allowFullProduct) || // 不拾取根结点
        !allowProduct
      ) {
        return;
      }

      if (
        target.parent !== docTree &&
        isEqual(allowPickType, [PickInputValueType.Product])
      ) {
        const pickObj = generateProductPickObj(
          get1stLevelPartFromId(target.key),
        ) as T;

        onPickNew([pickObj]);

        return;
      }

      if (
        (!allowPickInvisibleAssembleFromLeftPanel &&
          target.parent?.key === docTree.key &&
          !target.visible) ||
        (target.parent != null &&
          target.parent?.key !== docTree.key &&
          !target?.recursivelyVisible) // 不拾取不可见对象
      ) {
        return;
      }

      const pickObj = generateProductPickObj(target) as T;

      if (pickObj) {
        onPickNew([pickObj]);
      }
    },
  );

  const unRegister = useMemoizedFn(() => {
    setIsActive(false);

    userEvents.off(PickInputEvents.Input3dFocused, unRegister);

    csEvent()
      .off(CENTEREVENTS.POINTERMOVE, onMouseMove)
      .off(CENTEREVENTS.POINTERUP, onMouseUp)
      .off(CENTEREVENTS.POINTERDOWN, onMouseDown);

    userEvents.off(DocumentEvents.SELECT_TREE_OBJECT, onTreeViewSelected);
    userEvents.off(CAD_EVENT.SELECT_TREE_OBJECTS, onTreeViewMultiSelected);
    userEvents.off(
      PRODUCT_EVENT.ASSEMBLY_TREE_NODE_SELECTED,
      onAssemblyTreeViewSelected,
    );
  });

  const register = () => {
    if (csEvent().state === STATE.DEFAULT) {
      console.warn(
        '当前eventCenter状态为default，清检查是否未在命令中使用useCommand或csEvent().setState',
      );
    }
    unRegister();

    userEvents.emit(PickInputEvents.Input3dFocused);
    userEvents.on(PickInputEvents.Input3dFocused, unRegister);

    if (disabled) {
      return;
    }

    setIsActive(true);

    csEvent()
      .on(CENTEREVENTS.POINTERMOVE, onMouseMove)
      .on(CENTEREVENTS.POINTERUP, onMouseUp)
      .on(CENTEREVENTS.POINTERDOWN, onMouseDown);

    userEvents.on(DocumentEvents.SELECT_TREE_OBJECT, onTreeViewSelected);
    userEvents.on(CAD_EVENT.SELECT_TREE_OBJECTS, onTreeViewMultiSelected);
    userEvents.on(
      PRODUCT_EVENT.ASSEMBLY_TREE_NODE_SELECTED,
      onAssemblyTreeViewSelected,
    );
  };

  if (DualController.dragging) {
    unRegister();
  }

  if (allowLawCurve) {
    const name = pickedLawCurve?.title;
    const uuid = pickedLawCurve?.dxid;
    return (
      <MouseActionInput
        onActiveFocus={() => {
          if (!isActive) {
            onFocus?.();
          }
          register();
        }}
        onActiveBlur={() => {
          if (isActive) {
            onBlur?.();
          }
          unRegister();
        }}
        disabled={disabled}
        allowSelectFromTreeView
        {...rest}
        active={isActive}
      >
        {uuid && (
          <div
            key={uuid}
            className='pick-input_base-select_selection'
          >
            <div
              className='break-all'
              title={name}
            >
              {name}
            </div>
            <div
              className={'ml-auto'}
              onClick={() => {
                const newResult = {
                  title: '',
                  dxid: '',
                };
                onCurvedLawPick?.(newResult, null, true);
                setPickedLawCurve(newResult);
              }}
            >
              <Icon
                tabIndex={0}
                name='commons/close'
              />
            </div>
          </div>
        )}
      </MouseActionInput>
    );
  }

  const filteredData = pickedData.filter((v) => v != null);

  return (
    <MouseActionInput
      onActiveFocus={() => {
        if (!isActive) {
          onFocus?.();
        }
        register();
      }}
      onActiveBlur={() => {
        if (isActive) {
          onBlur?.();
        }
        unRegister();
      }}
      disabled={disabled}
      allowSelectFromTreeView
      {...rest}
      active={isActive}
      titleSuffix={
        filteredData.length === 0 || !isMulti ? null : (
          <div
            className='cursor-pointer whitespace-nowrap'
            onClick={() => {
              onPickWithoutDuplicateProdcuct([]);
            }}
          >
            清空
          </div>
        )
      }
    >
      {filteredData.map((data) => {
        const name = nameRenderer(data);
        const uuid = getMultiPickResultUUID(data);

        const isEdge = [PickType.CURVE, PickType.LINE].includes(data.idInfo.t);

        const showReverse = isEdge && showArrow && curveReversable;

        return (
          <div
            key={uuid}
            className='pick-input_base-select_selection'
          >
            <div
              className='break-all'
              title={name}
            >
              {name}
            </div>

            {showReverse && (
              <div
                className='ml-auto mr-1'
                onClick={() => {
                  (data.assisObj as PickLine | PickCurve).reverse();
                  preview();

                  const newResult = [...pickedData];

                  onPickWithoutDuplicateProdcuct?.(newResult, null, false);
                  setPickedObjects(newResult);
                }}
              >
                <ArrowsAltOutlined className='cursor-pointer' />
              </div>
            )}

            <div
              className={showReverse ? '' : 'ml-auto'}
              onClick={() => {
                const newResult = pickedData.filter(
                  (p) => uuid !== getMultiPickResultUUID(p),
                );
                onPickWithoutDuplicateProdcuct?.(newResult, null, true);
                setPickedObjects(newResult);
              }}
            >
              <Icon
                tabIndex={0}
                name='commons/close'
              />
            </div>
          </div>
        );
      })}
    </MouseActionInput>
  );
}
